﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Collections;
using System.Xml;
using System.Diagnostics;

namespace Lilium.Configuration
{
	/// <summary>
	/// Базовый класс для создания типизированных коллекций конфигурационных элементов
	/// </summary>
	/// <typeparam name="TConfigurationElement">Тип конфигурационного элемента</typeparam>
	public abstract class ConfigurationElementCollection<TConfigurationElement>: ConfigurationElementCollection, IEnumerable, IEnumerable<TConfigurationElement>
		where TConfigurationElement: ConfigurationElement
	{
		public TConfigurationElement this[int index]
		{
			[DebuggerNonUserCode]
			get
			{
				return GetElement(index);
			}
		}

		public TConfigurationElement this[object key]
		{
			[DebuggerNonUserCode]
			get
			{
				return GetElement(key);
			}
		}

		public void Add(TConfigurationElement element)
		{
			BaseAdd(element);
		}

		public new IEnumerator<TConfigurationElement> GetEnumerator()
		{
			for (var i = 0; i < Count; i++)
				yield return GetElement(i);
		}

		#region public event EventHandler<ResolveUnrecognizedElementEventArgs> ResolveUnrecognizedElement

		public event EventHandler<ResolveUnrecognizedElementEventArgs> ResolveUnrecognizedElement;

		protected virtual void OnResolveUnrecognizedElement(ResolveUnrecognizedElementEventArgs e)
		{
			if (e == null)
				throw new ArgumentNullException("e");
			EventHandler<ResolveUnrecognizedElementEventArgs> eventHandler = ResolveUnrecognizedElement;
			if (eventHandler != null)
				eventHandler(this, e);
		}

		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			for (var i = 0; i < Count; i++)
				yield return GetElement(i);
		}

		#endregion

		protected override ConfigurationElement CreateNewElement()
		{
			throw new NotSupportedException(string.Format(
				Resources.Culture,
				Resources.ConfigurationElementCollection_PurePolimorphicCollectionDoesNotSupportElementCreation_0collectionType,
				GetType()));
		}

		/// <summary>
		/// Возвращает элемент по индексу.
		/// </summary>
		/// <param name="index">
		/// Индекс конфигурационного элемента.
		/// </param>
		/// <returns>
		/// Конфигурационный элемент по указанному индексу.
		/// </returns>
		/// <remarks>
		/// Все операции обращения к элементам перенаправляются на переопределенные варианты виртуальных методов
		/// <see cref="ConfigurationElementCollection`1.GetElement()"/>.
		/// Перегружая эти методы в классах-наследниках можно выполнять дополнительную (позднюю) инициализацию 
		/// элементов коллекции, это используется в некоторых сценариях конфигурации.
		/// </remarks>
		protected virtual TConfigurationElement GetElement(int index)
		{
			return (TConfigurationElement)BaseGet(index);
		}

		/// <summary>
		/// Возвращает элемент по ключу.
		/// </summary>
		/// <param name="index">
		/// Ключ конфигурационного элемента.
		/// </param>
		/// <returns>
		/// Конфигурационный элемент с указанным ключем.
		/// </returns>
		/// <remarks>
		/// Все операции обращения к элементам перенаправляются на переопределенные варианты виртуальных методов
		/// <see cref="ConfigurationElementCollection`1.GetElement()"/>.
		/// Перегружая эти методы в классах-наследниках можно выполнять дополнительную (позднюю) инициализацию 
		/// элементов коллекции, это используется в некоторых сценариях конфигурации.
		/// </remarks>
		protected virtual TConfigurationElement GetElement(object key)
		{
			return (TConfigurationElement)BaseGet(key);
		}

		protected override bool OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
		{
			var resolveUnrecognizedElementEventArgs = new ResolveUnrecognizedElementEventArgs(elementName, reader);
			OnResolveUnrecognizedElement(resolveUnrecognizedElementEventArgs);
			if (resolveUnrecognizedElementEventArgs.IsResolved)
			{
				BaseAdd(resolveUnrecognizedElementEventArgs.ResolvedElement);
				return true;
			}
			else
				return base.OnDeserializeUnrecognizedElement(elementName, reader);
		}
	}
}
